#include <jni.h>
#include <string>
#include "MD5.h"
#include <iostream>

using namespace std;



//签名校验 为1的时候通过
static int IS_VERIFY = 0;

static char const *PN = "com.rlz.framemm";

static char const *SV = "3082035130820239a003020102020430c4ac93300d06092a864886f70d01010b05003059310b3009060355040613024348310e300c060355040813054368696e613110300e060355040713074265694a696e67310c300a060355040a1303727863310c300a060355040b1303727863310c300a06035504031303727863301e170d3231313132363034333931375a170d3436313132303034333931375a3059310b3009060355040613024348310e300c060355040813054368696e613110300e060355040713074265694a696e67310c300a060355040a1303727863310c300a060355040b1303727863310c300a0603550403130372786330820122300d06092a864886f70d01010105000382010f003082010a0282010100a3bc165b2aa01c0e152dcd30f55a2d1143da147c538f01cbb975effe49d578538cdd2749210f1318f74fff029fefc46026e6d9f59fa3a30341da9c135710e4761e5ae387e6517d4ba1985598736ce37dec01b78270392b51d589776bd09b579d96bebbe6616020caee7d6de51a682175d09a333e708aa2aa99ffb8c475f793367f7558dfe3954824f4f4d944431dcc68a18ee56f98881b4a930229b708f322efb552c0191dab347ca1342aaaa4a3f589ce6194c249e3e178d5e4adcd4566dff6526054ca5243eff8ccaad0478b6424b29132a31a4869334afe8d5b918f096a1db27a282eccef90c0f0f8d24301220c8bc2f4e8b3bc45d169e3849515a1cb1edd0203010001a321301f301d0603551d0e0416041404f4728ff1bd28459163ec9e21b7ad399912ed94300d06092a864886f70d01010b05000382010100706917a3363357aa45cd97503f10f0b00f445dc575238eefec2ebb67399a82f93f01b917028df268d2d728c7b80fb62beb5823a4e2cfce3b7a96f543b6c70159147cc706f48998b6c88db690ccd3cc4c9196193c0432ee507152c6a895e46947ffce3b3a7b4eec38f04f042903e1e7a96d6e7dd76a5757b7a6b5d92bc51e37e8664bc8aa6fe73d47b82cc1d94204eac86fa2f9363dfa1a30577d11e9c35edaa19587ba60f8e9f86a05a2ea4d66ef3c7ec3cf79c8bea8f910ccefdf3874750d23bca44cb34d4d075f86a172fa515441de6b4d79bebfe97c4bbf28a8629b84c449995a2c391d404ff45e698a7cf8b17351bf8f42f74aa38e8c83961b2f665bbeb0";

extern "C" {
jstring encryption(JNIEnv *env, jclass, jstring);
jstring addEncryption(JNIEnv *env, jclass clazz, jstring sing_str, jstring add_str, jint sub_len);
void verifySign(JNIEnv *env, jclass clazz, jobject context);
}

JNINativeMethod method[] = {
        {"toMD5",      "(Ljava/lang/String;)Ljava/lang/String;",                    (void *) encryption},
        {"toMD5",      "(Ljava/lang/String;Ljava/lang/String;I)Ljava/lang/String;", (void *) addEncryption},
        {"signVerify", "(Landroid/content/Context;)V",                              (void *) verifySign},
};

jint registerNativeMethod(JNIEnv *env) {
    jclass cl = env->FindClass("com/rlz/framemm/util/MD5");
    if ((env->RegisterNatives(cl, method, sizeof(method) / sizeof(method[0]))) < 0) {
        return -1;
    }
    return 0;
}

jint unRegisterNativeMethod(JNIEnv *env) {
    jclass cl = env->FindClass("com/rlz/framemm/util/MD5");
    env->UnregisterNatives(cl);
    return 0;
}

JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env;
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) == JNI_OK) {
        registerNativeMethod(env);
        return JNI_VERSION_1_6;
    } else if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) == JNI_OK) {
        registerNativeMethod(env);
        return JNI_VERSION_1_4;
    }
    return JNI_ERR;
}

JNIEXPORT void JNI_OnUnload(JavaVM *vm, void *reserved) {
    JNIEnv *env;
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) == JNI_OK) {
        unRegisterNativeMethod(env);
    } else if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) == JNI_OK) {
        unRegisterNativeMethod(env);
    }
}

/**
 * 简单的MD5加密
 */
extern "C"
JNIEXPORT jstring encryption(JNIEnv *env, jclass clazz, jstring sing_str) {
    if (IS_VERIFY == 0) return env->NewStringUTF("签名校验出错");
    return addEncryption(env, clazz, sing_str, nullptr, 0);
}

/**
 * add_str 为追加的字符
 * syb_len 为截取的位置 (末尾)
 */
extern "C"
JNIEXPORT jstring
addEncryption(JNIEnv *env, jclass clazz, jstring sing_str, jstring add_str, jint sub_len) {

    if (IS_VERIFY == 0) return env->NewStringUTF("签名校验出错");

    const char *neededStr;

    if (add_str != nullptr) {

        //使用静态方式 缓存id JNI执行速度快 但是java调用jni速度慢
        //多线程访问本地方法 ID 是相同的
        static jmethodID  methodId = nullptr;

        if (methodId == nullptr) {
            //通过java的方式拼接字符串
            jclass String_clazz = env->FindClass("java/lang/String");

            methodId = env->GetMethodID(String_clazz, "concat",
                                        "(Ljava/lang/String;)Ljava/lang/String;");
        }
        //处理拼接
        jobject concatStr = env->CallObjectMethod(add_str, methodId, sing_str);

        neededStr = env->GetStringUTFChars((jstring) concatStr, nullptr);

        env->ReleaseStringUTFChars((jstring) concatStr, neededStr);

        env->DeleteLocalRef(concatStr);
    } else {
        neededStr = env->GetStringUTFChars(sing_str, nullptr);

        env->ReleaseStringUTFChars(sing_str, neededStr);
    }


    string result = MD5(neededStr).hexdigest();

    if (result.length() > sub_len && sub_len != 0)
        result = result.substr(0, result.length() - sub_len);


    return env->NewStringUTF(result.c_str());
}

/**
 * 对签名进行校验
 * 这个地方不会频繁 调用可以省略缓存
 */
extern "C"
JNIEXPORT void verifySign(JNIEnv *env, jclass clazz, jobject context) {

    //获取包名
    jclass jclazz = env->GetObjectClass(context);
    jmethodID jId = env->GetMethodID(jclazz, "getPackageName", "()Ljava/lang/String;");
    auto j_package_name = (jstring) env->CallObjectMethod(context, jId);
    const char *c_name = env->GetStringUTFChars(j_package_name, nullptr);

    if (strcmp(c_name, PN) != 0) {
        return;
    }


    //获取签名
    // 1 获取 PackageManager
    jId = env->GetMethodID(jclazz, "getPackageManager", "()Landroid/content/pm/PackageManager;");
    jobject pm = env->CallObjectMethod(context, jId);

    // 2 获取packageInfo
    jclazz = env->GetObjectClass(pm);
    jId = env->GetMethodID(jclazz, "getPackageInfo",
                           "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
    jobject package_info = env->CallObjectMethod(pm, jId, j_package_name, 0x00000040);

    // 3 获取 signatures 数组
    jclazz = env->GetObjectClass(package_info);
    jfieldID fieldId = env->GetFieldID(jclazz, "signatures", "[Landroid/content/pm/Signature;");
    auto signatures = (jobjectArray) env->GetObjectField(package_info, fieldId);

    // 4 获取数组中的值
    jobject signValve = env->GetObjectArrayElement(signatures, 0);

    //5 调用toCharsString方法获取值
    jclazz = env->GetObjectClass(signValve);
    jId = env->GetMethodID(jclazz, "toCharsString", "()Ljava/lang/String;");
    auto signature_str = (jstring) env->CallObjectMethod(signValve, jId);

    const char *c_signature_str = env->GetStringUTFChars(signature_str, nullptr);

    // 6 对比签名
    if (strcmp(c_signature_str, SV) != 0) {
        return;
    }

    // 签名认证成功
    IS_VERIFY = 1;

}
